HTML 语义化与新特性
发表于:2026-07-29
字数统计:7000 字
预计阅读24分钟
涵盖 HTML 语义化标签对比、HTML5 新特性、H5 移动端适配、viewport、Web Components、Shadow DOM、跨组件通信、自定义元素生命周期、slot 插槽、template 模板与原生组件库。
一、HTML 语义化
1. 为什么要语义化?
HTML
<!-- ❌ 不语义化:用无语义的 div 堆砌 -->
<div class="header">
<div class="nav">...</div>
</div>
<div class="main">
<div class="article">
<div class="title">标题</div>
<div class="content">内容</div>
</div>
</div>
<!-- ✅ 语义化:用有意义的标签 -->
<header>
<nav>...</nav>
</header>
<main>
<article>
<h1>标题</h1>
<p>内容</p>
</article>
</main>语义化优点:
- 可读性:代码结构清晰,开发者一眼能看明白页面结构
- 可维护性:样式、行为解耦,更易于维护
- SEO 友好:搜索引擎爬虫依赖语义化标签理解页面结构
- 可访问性:屏幕阅读器等辅助技术依赖语义化标签
- 团队协作:统一的语义约定便于沟通
2. 常用语义化标签
| 标签 | 用途 | 替代方案 |
|---|---|---|
<header> | 页头 / 区块头部 | <div class="header"> |
<footer> | 页脚 / 区块底部 | <div class="footer"> |
<nav> | 导航链接 | <div class="nav"> |
<main> | 主内容(每个页面唯一) | <div class="main"> |
<article> | 独立文章内容 | <div class="article"> |
<section> | 主题性区块 | <div class="section"> |
<aside> | 侧边栏 / 注释 | <div class="sidebar"> |
<figure> / <figcaption> | 图片与说明 | <div class="img-wrapper"> |
<time> | 时间 | <span class="time"> |
<mark> | 高亮文本 | <span class="highlight"> |
<address> | 联系信息 | <div class="address"> |
3. 关键规则
HTML
<!-- 每个页面只一个 <main> -->
<main>
<!-- <article> 是独立可分发的内容(博客文章、新闻、帖子) -->
<article>
<header>
<h1>文章标题</h1>
<time datetime="2024-05-15">2024年5月15日</time>
</header>
<p>文章内容...</p>
<figure>
<img src="..." alt="..." />
<figcaption>图片说明</figcaption>
</figure>
</article>
<!-- <section> 是主题性分组 -->
<section>
<h2>相关推荐</h2>
<article>...</article>
</section>
</main>
<aside>
<!-- 侧边栏 -->
</aside>
<footer>
<address>联系邮箱:xxx@example.com</address>
</footer>注意:
<section>一定要有标题(h1~h6),否则考虑用<div><article>比<section>独立性更强:article 脱离上下文仍能完整表达含义- 不要用
<section>替代<div>当作普通容器
二、HTML5 新特性
1. 新增语义化标签
HTML
<!-- 结构相关 -->
<header>, <footer>, <nav>, <main>, <article>, <section>, <aside>, <figure>, <figcaption>, <time>, <mark>, <address>, <details>, <summary>, <dialog>
<!-- 文本相关 -->
<ruby>, <rt>, <rp> <!-- 注音 -->
<!-- 媒体 -->
<audio>, <video>, <source>, <track>, <picture>
<!-- 交互 -->
<canvas>, <details>, <dialog>
<!-- 表单 -->
<input type="date|email|number|range|search|tel|url|color|file">
<!-- 其他 -->
<output>, <progress>, <meter>, <template>, <slot>2. 表单新特性
HTML
<!-- placeholder:占位符 -->
<input type="text" placeholder="请输入用户名" />
<!-- required:必填 -->
<input type="text" required />
<!-- pattern:正则验证 -->
<input type="text" pattern="[A-Za-z]{3,}" />
<!-- type 新类型 -->
<input type="email" />
<input type="url" />
<input type="number" min="1" max="100" step="1" />
<input type="range" min="0" max="100" />
<input type="date" />
<input type="color" />
<!-- autocomplete -->
<input type="text" autocomplete="username" />
<!-- autofocus -->
<input type="text" autofocus />
<!-- datalist:下拉建议 -->
<input type="text" list="browsers" />
<datalist id="browsers">
<option value="Chrome" />
<option value="Firefox" />
<option value="Safari" />
</datalist>3. 多媒体
HTML
<!-- audio -->
<audio controls>
<source src="audio.mp3" type="audio/mpeg" />
<source src="audio.ogg" type="audio/ogg" />
您的浏览器不支持 audio 标签。
</audio>
<!-- video -->
<video controls width="640" height="360" poster="poster.jpg">
<source src="video.mp4" type="video/mp4" />
<source src="video.webm" type="video/webm" />
<track src="captions.vtt" kind="subtitles" srclang="zh" label="中文" />
您的浏览器不支持 video 标签。
</video>
<!-- picture:响应式图片 -->
<picture>
<source media="(max-width: 600px)" srcset="small.jpg" />
<source media="(max-width: 1200px)" srcset="medium.jpg" />
<img src="large.jpg" alt="..." />
</picture>4. Canvas 与 SVG
HTML
<!-- Canvas:位图 -->
<canvas id="myCanvas" width="200" height="100"></canvas>
<!-- SVG:矢量图 -->
<svg viewBox="0 0 100 100">
<circle cx="50" cy="50" r="40" stroke="black" stroke-width="3" fill="red" />
</svg>5. Web Storage
JavaScript
// localStorage:永久存储(同源 5-10 MB)
localStorage.setItem('user', JSON.stringify({ name: '张三' }))
localStorage.getItem('user')
// sessionStorage:会话级(关闭标签页即清空)
sessionStorage.setItem('token', 'xxx')
// 存储事件监听(同源其他页面修改 storage 触发)
window.addEventListener('storage', (e) => {
console.log(e.key, e.oldValue, e.newValue)
})6. Web Workers
JavaScript
// 主线程
const worker = new Worker('worker.js')
worker.postMessage({ cmd: 'compute', data: 1000000000 })
worker.onmessage = (e) => {
console.log('计算结果:', e.data)
}
// worker.js(独立线程)
self.onmessage = (e) => {
const data = e.data.data
let sum = 0
for (let i = 0; i < data; i++) sum += i
self.postMessage(sum)
}7. WebSocket
JavaScript
const ws = new WebSocket('wss://example.com/socket')
ws.onopen = () => ws.send('hello')
ws.onmessage = (e) => console.log('收到消息:', e.data)
ws.onclose = () => console.log('连接关闭')
ws.onerror = (err) => console.error('错误:', err)8. Geolocation / Notification / Drag&Drop
JavaScript
// Geolocation API
navigator.geolocation.getCurrentPosition(
pos => console.log(pos.coords.latitude, pos.coords.longitude),
err => console.error(err)
)
// Notification API
Notification.requestPermission().then(permission => {
if (permission === 'granted') {
new Notification('标题', { body: '内容' })
}
})
// Drag & Drop API
// 详见后文三、H5 移动端适配
1. viewport
HTML
<!-- 常用且允许用户缩放的 viewport 配置 -->
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />| 属性 | 含义 |
|---|---|
width=device-width | 视口宽度等于设备宽度 |
initial-scale=1.0 | 初始缩放比例 |
maximum-scale | 最大缩放 |
user-scalable | 是否允许用户缩放;普通页面不应禁用,以保障低视力用户可访问性 |
viewport-fit=cover | 适配刘海屏(iOS) |
2. 移动端事件
JavaScript
// touchstart / touchmove / touchend
element.addEventListener('touchstart', (e) => {
e.touches // 当前屏幕上所有手指
e.targetTouches // 当前元素上的手指
e.changedTouches // 触发本次事件的手指
})
// 阻止默认行为(防止滚动、缩放)
element.addEventListener('touchmove', (e) => e.preventDefault(), { passive: false })
// 300ms 点击延迟问题
// 早期移动浏览器可能等待双击缩放判断;现代页面使用规范 viewport 后通常没有该问题
// 不要把 click 机械替换为 touchend,也不再建议引入 FastClick;应保留键盘/辅助技术可用的语义化点击行为3. rem / vw 适配
CSS
/* rem 方案(需要 JS 动态设置 html font-size) */
html { font-size: 14px; }
@media (min-width: 320px) { html { font-size: 14px; } }
@media (min-width: 375px) { html { font-size: 16.4px; } }
@media (min-width: 414px) { html { font-size: 18.4px; } }
.box { width: 10rem; height: 10rem; }
/* vw 方案(推荐,纯 CSS) */
html { font-size: calc(100vw / 7.5); } /* 假设设计稿 750px */
.box { width: 1rem; height: 1rem; }4. 1px 边框问题
CSS
/* 物理像素和 CSS 像素在 dpr=2 的设备上不一致
CSS 的 1px 在屏幕上实际显示 2 物理像素
解决:用 transform 缩放 */
.border-1px {
position: relative;
}
.border-1px::after {
content: '';
position: absolute;
left: 0; right: 0; bottom: 0;
height: 1px;
background: #000;
transform: scaleY(0.5);
transform-origin: 0 0;
}
/* 或用 box-shadow */
.box { box-shadow: inset 0 -1px 0 0 #000; }四、Web Components
Web Components 是一组浏览器原生支持的组件化方案,包含四个核心规范:
- Custom Elements(自定义元素)
- Shadow DOM(影子 DOM)
<template>和<slot>- ES Modules
1. Custom Elements(自定义元素)
JavaScript
// 1. 注册自定义元素
class MyButton extends HTMLElement {
constructor() {
super()
this.attachShadow({ mode: 'open' })
this.shadowRoot.innerHTML = `
<style>
button {
background: #4CAF50;
color: white;
padding: 10px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
}
</style>
<button><slot></slot></button>
`
}
connectedCallback() {
// 元素被添加到 DOM 时调用
console.log('添加到 DOM')
}
disconnectedCallback() {
// 元素从 DOM 移除时调用
console.log('从 DOM 移除')
}
attributeChangedCallback(name, oldValue, newValue) {
// 观察的属性变化时调用
console.log(`${name}: ${oldValue} → ${newValue}`)
}
static get observedAttributes() {
return ['color', 'size']
}
}
customElements.define('my-button', MyButton)HTML
<!-- 使用 -->
<my-button color="red">点击我</my-button>2. Shadow DOM
JavaScript
// 创建 Shadow DOM
class MyCard extends HTMLElement {
constructor() {
super()
const shadow = this.attachShadow({ mode: 'open' })
shadow.innerHTML = `
<style>
.card {
padding: 16px;
background: #fff;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
</style>
<div class="card">
<slot name="title"></slot>
<slot></slot>
</div>
`
}
}
customElements.define('my-card', MyCard)Shadow DOM 的隔离特性:
- Shadow DOM 内部的样式不污染外部
- 外部普通选择器不会穿透 shadow tree,但继承属性、CSS 自定义属性、
::part()、:host等机制仍可影响内部样式 mode: 'open'时外部脚本可通过shadowRoot访问内部;closed只让element.shadowRoot返回null,不是强安全边界,持有内部引用或注入页面上下文的代码仍可能访问
HTML
<!-- 外部样式想影响 Shadow DOM:用 CSS variables -->
<style>
my-card {
--card-bg: #f5f5f5;
--card-padding: 20px;
}
</style>CSS
/* Shadow DOM 内部使用变量 */
.card {
background: var(--card-bg, white);
padding: var(--card-padding, 16px);
}3. Template 和 Slot
HTML
<template id="myTemplate">
<style>
.wrapper {
padding: 16px;
}
</style>
<div class="wrapper">
<h2><slot name="title">默认标题</slot></h2>
<div><slot>默认内容</slot></div>
</div>
</template>JavaScript
class MyPanel extends HTMLElement {
constructor() {
super()
const template = document.getElementById('myTemplate')
const templateContent = template.content
this.attachShadow({ mode: 'open' })
this.shadowRoot.appendChild(templateContent.cloneNode(true))
}
}
customElements.define('my-panel', MyPanel)HTML
<my-panel>
<span slot="title">自定义标题</span>
<p>这是自定义内容</p>
</my-panel>4. Web Components 的生命周期
| 钩子 | 调用时机 |
|---|---|
constructor() | 创建元素实例时 |
connectedCallback() | 元素被添加到 DOM |
disconnectedCallback() | 元素从 DOM 移除 |
attributeChangedCallback() | 观察的属性变化 |
adoptedCallback() | 元素被 adoptNode 移动到新文档 |
5. Web Components 优缺点
优点:
- 原生支持:无需框架,原生组件化
- 样式隔离:Shadow DOM 防止样式冲突
- 框架无关:可与任何框架共存
- 轻量:运行时开销小
缺点:
- 生态弱:相比 Vue/React 缺少成熟的工具链
- 状态管理弱:没有内置响应式系统
- SSR 难:服务端渲染需要 polyfill
- 样式穿透繁琐:需要 CSS variables / ::part()
- 学习成本:API 较新,文档相对少
6. 跨组件通信
JavaScript
// 方式1:CustomEvent
class ChildEl extends HTMLElement {
fireEvent() {
this.dispatchEvent(new CustomEvent('my-event', {
detail: { msg: 'hello' },
bubbles: true, // 是否冒泡
composed: true // 是否可穿透 Shadow DOM 边界
}))
}
}
document.querySelector('child-el').addEventListener('my-event', (e) => {
console.log('收到:', e.detail.msg)
})
// 方式2:属性 + 监听
childEl.setAttribute('data-value', 'hello')
// 方式3:共享 store
// 自定义一个事件总线,或使用 window 自定义事件五、面试高频问答
Q1: 什么是 HTML 语义化?为什么要语义化?
答:HTML 语义化是使用恰当的 HTML 标签来表达内容的结构和含义(如 <header> 表示头部、<article> 表示文章),而不是用无语义的 <div> 堆砌。优点:
- 代码可读性和可维护性
- SEO 友好(搜索引擎更准确地理解页面)
- 可访问性(屏幕阅读器依赖语义化标签)
- 团队协作的统一约定
Q2: <section> 和 <article> 怎么区分?
答:
<article>:独立可分发的内容(博客文章、新闻、帖子)。脱离上下文仍能完整表达含义。<section>:主题性分组(页面中的几个章节)。脱离上下文可能含义不完整。
简单记忆:能用 RSS 订阅的内容用 <article>,其他用 <section>。
Q3: Shadow DOM 解决了什么问题?
答:Shadow DOM 提供了样式和 DOM 的封装隔离。在大型应用或第三方组件库场景下:
- 组件内部样式不污染外部(防止全局污染)
- 外部样式也不会意外影响组件内部(防止被覆盖)
- 避免类名冲突(不需要 CSS Modules / BEM)
Q4: Web Components 和 Vue 组件有什么区别?
答:
| 维度 | Web Components | Vue 组件 |
|---|---|---|
| 框架依赖 | 原生无依赖 | 需要 Vue |
| 响应式 | 需要手动实现 | 内置响应式 |
| 生态 | 较弱 | 成熟 |
| 样式隔离 | Shadow DOM | scoped / CSS Modules |
| 学习成本 | 新 API | Vue 心智模型 |
| SSR | 难 | 完善 |
| 最佳场景 | 跨框架共享的微组件 | 应用级组件库 |
现代方案通常是混合:跨框架的基础组件用 Web Components,应用层用 Vue/React。
Q5: viewport 中 width=device-width 的意义?
答:在不设置 viewport 的情况下,移动浏览器会假设页面是桌面端(宽度 980px),导致页面被缩放显示。width=device-width 让 viewport 宽度等于设备宽度,CSS 像素与设备像素一一对应(在高 DPR 设备上仍受 dpr 影响),从而实现正常的移动端布局。